/********************************************************************
 * File: instrument.c
 *
 * gctrace source -- link this with your application.
 *
 * Author: Frank.Li <lgl88911@163.com>
 *
 */


#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <assert.h>

#include "talloc.h"
#include "rbtree.h"

#define USE_TREE


#define show_msg(...)	printf(__VA_ARGS__)

#define osget_tid()		syscall(SYS_gettid)
#define osget_tick()	get_tick();

/* Function prototypes with attributes */
void main_constructor( void )
	__attribute__ ((no_instrument_function, constructor));

void main_destructor( void )
	__attribute__ ((no_instrument_function, destructor));

void __cyg_profile_func_enter( void *, void * ) 
	__attribute__ ((no_instrument_function));

void __cyg_profile_func_exit( void *, void * )
	__attribute__ ((no_instrument_function));

int trbt_compare(void *p1, void *p2)
	__attribute__ ((no_instrument_function));

static void *trbt_malloc(size_t size)
	__attribute__ ((no_instrument_function));

static void trbt_free(void *ptr)
	__attribute__ ((no_instrument_function));

static void *trbt_realloc(void *ptr, size_t size)
	__attribute__ ((no_instrument_function));


/*Set max thread num*/
#define MAX_THREAD_NUM	50
#define STACK_SIZE	100

#define INIT_LOCK()	pthread_mutex_init(&mutexlist, NULL )
#define LOCK_LIST()	pthread_mutex_lock(&mutexlist)
#define UNLOCK_LIST()	pthread_mutex_unlock(&mutexlist)

#define INIT(_nt)	{_nt =0;}
#define PUSH(_s, _nt,  _e)	{assert(_nt<STACK_SIZE);_s[_nt] = _e; _nt++;}
#define POP(_s, _nt, _e)	{assert(_nt>0); _nt--; _e = _s[_nt];}
#define TOP(_s, _nt, _e)	{assert(_nt>0); _e = _s[_nt-1];}

typedef struct _st_trace_node
{
		struct _st_trace_node *next;
		
		void *father;
		void *me;
		unsigned int ms;
}st_trace_node;

typedef struct _st_gtrace_node
{
	void *father;
	void *me;
	int cnt;
}st_gtrace_node;

typedef struct
{
	pid_t pid;
#ifdef USE_TREE
	TALLOC_CTX *mem_ctx;
	trbt_tree_t *tree;
	void *stack[STACK_SIZE];
	int nexttop;
#else
	FILE *fp;
#endif
}st_trace_list;

st_trace_list listgroup[MAX_THREAD_NUM];
pthread_mutex_t mutexlist;

int trbt_compare(void *p1, void *p2)
{
	st_gtrace_node *new = (st_gtrace_node*)p1;
	st_gtrace_node *old = (st_gtrace_node*)p2;

	//printf("cmp %p(%p)-%p(%p)  ", new->father,old->father, new->me, old->me);
	if(new->father == old->father)
	{
		if(new->me == old->me)
		{
			//printf("=\n");
			return TRBT_EQ;
		}
		
		if(new->me < old->me)
		{
			//printf("<\n");
			return TRBT_LT;
		}
		
		if(new->me > old->me)
		{
			//printf(">\n");
			return TRBT_RT;
		}
	}
	
	if(new->father < old->father)
	{
		//printf("<\n");
		return TRBT_LT;
	}
	
	if(new->father > old->father)
	{
		//printf(">\n");
		return TRBT_RT;
	}
}

void main_constructor( void )
{
	int i;
	INIT_LOCK();

	for(i=0; i<MAX_THREAD_NUM; i++)
	{
		listgroup[i].pid = -1;
		#ifdef USE_TREE
		listgroup[i].mem_ctx = NULL;
		listgroup[i].tree = NULL;
		#else
		listgroup[i].fp = NULL;
		#endif
	}
	
	printf("Trace init\n");
}

void traverse_write(void *param, void *data)
{
	FILE *fp = param;
	
	fwrite(data, sizeof(st_gtrace_node), 1, fp);
}

void main_destructor( void )
{
	int i;
	
	LOCK_LIST();
	for(i=0; i<MAX_THREAD_NUM; i++)
	{
		#ifdef USE_TREE
		if(listgroup[i].tree != NULL)
		{
			FILE *fp;
			char cfile[10];
			sprintf(cfile, "%d.gc", listgroup[i].pid);
			fp = fopen(cfile, "wb" );
		  	if(fp == NULL)
			{
				printf("Open file %s fail\n", cfile);
			}
			else
			{
				trbt_traverse(listgroup[i].tree, traverse_write, fp);
				fclose(fp);
			}
		}
		#else
		if(listgroup[i].fp != NULL)
		{
			fclose(listgroup[i].fp);
			listgroup[i].fp = NULL;
		}
		#endif
	}
	UNLOCK_LIST();
	printf("Trace end\n");
}


void __cyg_profile_func_enter( void *this, void *callsite )
{
	int i;
	int index = -1;
	pid_t pid = osget_tid();
	
	LOCK_LIST();
	for(i=0; i<MAX_THREAD_NUM; i++)
	{
		if(listgroup[i].pid == pid)
		{
			index = i;
			break;
		}

		if(index == -1)
		{
			if(listgroup[i].pid == -1)
			{
				index = i;
			}
		}
	}

	#ifdef USE_TREE
	if(listgroup[index].pid == -1)
	{
		listgroup[index].mem_ctx = talloc_new(NULL);
		if(listgroup[index].mem_ctx == NULL)
		{
			goto _exit;
		}

		listgroup[index].tree = trbt_create(listgroup[index].mem_ctx, trbt_compare, 0);
		printf("Create tree %p\n", listgroup[index].tree);
		if(listgroup[index].tree == NULL)
		{
			talloc_free(listgroup[index].mem_ctx);
			goto _exit;
		}
		listgroup[index].pid = pid;
		INIT(listgroup[index].nexttop);
		PUSH(listgroup[index].stack, listgroup[index].nexttop, callsite);
	}
	
	if(index != -1)
	{
		st_gtrace_node *cur = talloc(NULL, st_gtrace_node);
		//cur->father = callsite;
		TOP(listgroup[index].stack, listgroup[index].nexttop, cur->father);
		PUSH(listgroup[index].stack, listgroup[index].nexttop, this);
		cur->me = this;
		
		cur->cnt = 1;
		//printf("malloc node %p\n", cur);
		st_gtrace_node *node = trbt_lookup(listgroup[index].tree, cur);
		//printf("find node %p\n", node);
		if(node != NULL)
		{
			talloc_free(cur);
			node->cnt++;
			//printf("pid%d[a]:%p call %p\n", listgroup[index].pid, cur->father, cur->me);
		}
		else
		{
			trbt_insert(listgroup[index].tree, cur);
			//printf("pid%d[n]:%p call %p\n", listgroup[index].pid, cur->father, cur->me);
			//printf("insert node %p\n", cur);
		}
	}
	#else
	if(listgroup[index].pid == -1)
	{
		char cfile[10];
		sprintf(cfile, "%d.txt", pid);
		listgroup[index].pid = pid;
		listgroup[index].fp = fopen(cfile, "w" );
	  	if (listgroup[index].fp == NULL)
		{
			printf("Open file %s fail\n", cfile);
		}
	}
	
	if(index != -1)
	{
		fprintf(listgroup[index].fp, "E%p\n", (int *)this);
		//fflush(listgroup[index].fp);
	}
	#endif

_exit:
	UNLOCK_LIST();
}


void __cyg_profile_func_exit( void *this, void *callsite )
{
	int i;
	int index = -1;
	pid_t pid = osget_tid();
	
	LOCK_LIST();

	for(i=0; i<MAX_THREAD_NUM; i++)
	{
		if(listgroup[i].pid == pid)
		{
			void *popdata;
			index = i;
			POP(listgroup[index].stack, listgroup[index].nexttop, popdata);
			break;
		}
	}
	#ifdef USE_TREE
	#else	
	if(index != -1)
	{
		fprintf(listgroup[index].fp, "X%p\n", (int *)this);
		//fflush(listgroup[index].fp);
	}
	#endif
	UNLOCK_LIST();
}


